<?php

namespace teamug;


/**
 * Controls the framework
 * Executes code according to the request
 * 
 * @author sideshowAnthony
 * 
 */
class Project {
    
  
  private static $appNamespace = null;
  private static $namespaceDirectories = array();
  
    
  public function __construct($namespace, $directory) {
    self::$appNamespace = $namespace;
    self::declareNamespace($namespace, $directory);
    if (Dev::$narration) {
      Dev::narrate('<pre>');
      Dev::narrate('Project starts.');
      Dev::narrate("App namespace:$namespace related to dir:$directory");
    }
  }
 
  
   /**
   * Tell teamug where a namespace can be found
   * Used by: Developer
   * 
   * @param string $namespace Eg 'app' which relates to directory
   * @param string $directory Directory of specified namespace. Eg '/var/www/my_site.com'
   */
  public function declareNamespace($namespace, $directory) {
    self::$namespaceDirectories[$namespace] = $directory;
    if (Dev::$narration) Dev::narrate("Namespace '$namespace' related to '$directory'");
  }
  

  
   /**
   * Gets app's namespace name, and directory
   * Used by: teamug
   * 
   * @return array Contains namespace name, and related directory
   */
  public static function getAppNamespace() {
    return array(
      'namespace'=>self::$appNamespace,
      'directory'=>self::$namespaceDirectories[self::$appNamespace]
      );
  }
  
  
   /**
   * Which directory to find a namespace in
   * Used by: teamug
   * 
   * @param string $namespace A developer pre-declared namespace. Eg 'app'
   * @return string Directory which relates to the namespace. Eg '/var/www/www.my_site.com'
   */
  public static function getNamespaceDirectory($namespace) {
    return self::$namespaceDirectories[$namespace];
  }

  
  
  /**
   * Convert's the URL into a class name and arguments
   * Instantiates the class (defined by the developer), and passes args to
   * get, post, delete, put... functions
   * Used by: Developer
   * 
   * @return object Type: \teamug\HtmlResponse or \teamug\JsonResponse etc
   */
  public function main() {

    $request_uri = $_SERVER['REQUEST_URI'];
    
    if (Dev::$narration) { Dev::narrate('Working from URI: '.$request_uri); }
    
    $class_name = $this->determineClassName($request_uri);
    $method = $this->determineMethod();
    $args = $this->determineArgs($request_uri);

    $error = $this->loadRequestHandler($class_name, $method);
    
    $obj = new $class_name;
    return $obj->$method($args);
  }
  
  
  
  
  /**
   * Determine class name from the URL
   * /show-product/22/elvis-alarm-clock will return 'ShowProduct'
   * / will return 'Index'
   * 
   * Used by: teamug
   * 
   * @param string $requestUri
   * @return string Class name
   */
  private function determineClassName($requestUri) {
    
    $parts = \explode('/', $requestUri); // [ '', 'show-product', '22', 'elvis-alarm-clock']
    
    \array_shift($parts); // [ 'show-product', '22', 'elvis-alarm-clock']
    
    $raw_class_name = array_shift($parts); // 'show-product'
    
    if (Dev::$narration) { Dev::narrateDump('raw_class_name', $raw_class_name); }
        
    if ($raw_class_name!=='') { 
      // 'show-product' > 'show product' > 'Show Product' > 'ShowProduct'
      $alpha_num = \preg_replace('[^A-Za-z0-9]', ' ', $raw_class_name);
      $ucased = ucwords($alpha_num);
      $class_name = \preg_replace('[^A-Za-z0-9]', '', $ucased);
    } else {
      $class_name = 'Index';
    } 
    if (Dev::$narration) { Dev::narrate('URL resolves to class: '.$class_name); }
    return $class_name;
  }
  
  
  
  /**
   * Determine arguments from the URL, to pass to request handler
   * /show-product/22/elvis-alarm-clock will return ['22', 'elvis-alarm-clock', null, null ... ]
   * / will return []
   * 
   * Used by: teamug
   * 
   * @param string $requestUri
   * @return array Args
   */
  private function determineArgs($requestUri) {
    
    $parts = explode('/', $requestUri); // [ '', 'show-product', '22', 'elvis-alarm-clock']
    
    \array_shift($parts); // [ 'show-product', '22', 'elvis-alarm-clock']
    \array_shift($parts); // [ '22', 'elvis-alarm-clock']

    $args = array_merge($parts, array(null,null,null,null,null,null,null,null,null,null));
    
    if (Dev::$narration) { Dev::narrateDump('URL provides args:', $args,true); }
    return $args;
  }
  
  
  
  /**
   * Determines the class method to call from the request method
   * Eg 'GET' = 'get'
   * Since browsers are limited to GET/POST, $_POST['_method']='DELETE' (eg) can be used
   * 
   * Used by: teamug
   * 
   * @return string The request method in lower case: 'get' / 'post' / 'delete'
   */
  private function determineMethod() {
    
    $method = $_SERVER['REQUEST_METHOD'];
    
    if ($method=='POST' && \array_key_exists('_method', $_POST)) {
      $class_method = \strtolower($_POST['_method']);
    } else {
      $class_method = \strtolower($method);
    }
    
    if (Dev::$narration) { Dev::narrate('Request method: '.$class_method); }
    return $class_method;
  }
 
  
  private function loadRequestHandler($className, $method) {
    
    $file_path = self::$namespaceDirectories[self::$appNamespace].'/'.$className.'.php';

    if (!\file_exists($file_path)) {
      if (Dev::$narration) { Dev::narrate('Can not find request handler file: '.$file_path); }
      return 'file not found';
    }

    require_once $file_path;

    $namespacedFilename = '/'.self::$appNamespace.'/'.$className;
    if(!\class_exists($namespacedFilename)) {
      if (Dev::$narration) { 
        Dev::narrate("Found request handler file '$file_path', but class '$namespacedFilename' is missing"); 
      }
        return 'class not found';
    }
    
  }
  
  
  
  
}
